package com.logistics.product.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.logistics.delivery.entity.domain.Delivery;
import com.logistics.delivery.entity.model.DeliverySave;
import com.logistics.delivery.service.DeliveryService;
import com.logistics.erp.entity.domain.ErpLackMaterial;
import com.logistics.erp.service.ErpOrderService;
import com.logistics.inventory.entity.domain.Inventory;
import com.logistics.inventory.service.InventoryService;
import com.logistics.map.entity.domain.MapComponents;
import com.logistics.map.service.MapComponentsConvertService;
import com.logistics.map.service.MapComponentsService;
import com.logistics.organization.service.OrganizationService;
import com.logistics.pick.entity.domain.Pick;
import com.logistics.pick.entity.domain.PickContainer;
import com.logistics.pick.entity.domain.PickDetailInvBill;
import com.logistics.pick.entity.domain.PickWorkshop;
import com.logistics.pick.entity.model.PickDetailRow;
import com.logistics.pick.service.PickContainerService;
import com.logistics.pick.service.PickDetailService;
import com.logistics.pick.service.PickService;
import com.logistics.product.entity.model.CallMaterialRow;
import com.logistics.product.entity.model.PickOrderDetailRow;
import com.logistics.product.mapper.CallMaterialMapper;
import com.logistics.qrcode.service.QrcodeService;
import com.logistics.utils.bean.UserBean;
import com.logistics.utils.constants.Constants;
import com.logistics.utils.exception.MessageException;
import com.logistics.utils.tool.StringUtils;
import com.logistics.utils.tool.UserUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class CallMaterialService extends ServiceImpl<CallMaterialMapper, Delivery> {

    @Autowired
    private PickService pickService;

    @Autowired
    private InventoryService inventoryService;

    @Autowired
    private DeliveryService deliveryService;

    @Autowired
    private MapComponentsService mapComponentsService;

    @Autowired
    private QrcodeService qrcodeService;

    @Autowired
    private ErpOrderService erpOrderService;

    @Autowired
    private UserUtils userUtils;

    @Autowired
    private OrganizationService organizationService;

    @Autowired
    private PickDetailService pickDetailService;

    @Autowired
    private PickContainerService pickContainerService;

    @Autowired
    private MapComponentsConvertService mapComponentsConvertService;

    /**
     * 查找拣配单列表
     * @return
     * @throws Exception
     */
    public List<CallMaterialRow> picks(String code) throws MessageException {
        siteVerification(code);
        UserBean userBean = userUtils.getUserInfo();
        // 根据id查找班组编码
        String wcCode = organizationService.wcCodeById(userBean.getPost());
        // 根据code查找站点区域
        String area = qrcodeService.areaByCode(code, Constants.CODE_TYPE_LOCATION);
        // 查找拣配单列表根据班组和区域
        List<PickWorkshop> pickWorkshops = pickService.pickByWcCodeAndArea(wcCode, area);
        return pickWorkshops.stream().map(CallMaterialRow::build).collect(Collectors.toList());
    }

    /**
     * 清单查看明细
     * @param pickId 拣配单号
     */
    public List<PickOrderDetailRow> detail(String pickId, String container) throws MessageException {
        Pick pick = pickService.getOne(new LambdaQueryWrapper<Pick>().eq(Pick::getId, pickId));
        if(StringUtils.isNull(pick)){
            throw new MessageException("拣配单不存在");
        }
        List<String> invBillCodes = pickDetailService.materials(PickDetailInvBill.build(pick.getId()))
                .stream().map(PickDetailInvBill::getErpInvBillCode).collect(Collectors.toList());
        // 缺料数据
        List<ErpLackMaterial> lackMaterials = erpOrderService.lackMaterialsInfo(invBillCodes);
        // 查找物料列表
        List<PickDetailRow> materials = pickDetailService.listByPickId(pickId, container);
        List<PickOrderDetailRow> materialList = materials.stream().map(e -> PickOrderDetailRow.build(e, pick.getStatus(), Constants.MATERIAL_LIST))
               .collect(Collectors.toList());
        List<PickOrderDetailRow> lackMaterialList = lackMaterials.stream().map(e -> PickOrderDetailRow.build(e, Constants.LACK_MATERIAL_LIST)).
                collect(Collectors.toList());
        lackMaterialList.addAll(materialList);
        return lackMaterialList;
    }

    /**
     * 验证扫描的站点是否正确
     * @param code
     * @throws Exception
     */
    private void siteVerification(String code) throws MessageException{
        //PDA扫描站点码 查询对应的站点位置
        String point = qrcodeService.valueByCode(code, Constants.CODE_TYPE_LOCATION);
        UserBean userBean = userUtils.getUserInfo();
        // 获取班组code
        String wcCode = organizationService.wcCodeById(userBean.getPost());
        //扫描站点码不属于当前用户所在班组
        List<MapComponents> mapComponents = mapComponentsService.list(new LambdaQueryWrapper<MapComponents>().isNotNull(MapComponents :: getLocationWcCode)
                .ne(MapComponents :: getLocationWcCode , wcCode).eq(MapComponents::getLocation, point));
        if (!mapComponents.isEmpty()){
            throw new MessageException("扫描站点码不属于当前用户所在班组");
        }
        // 判断站点是否存在物料
        List<Inventory> lgsPickContainerList = inventoryService.list(new LambdaQueryWrapper<Inventory>().eq(Inventory::getStation, point));
        //站点存在物料：画面报错，报错信息：站点已经存在物料，请勿重复叫料。
        if (!lgsPickContainerList.isEmpty()){
            //站点已存在物料，请勿重复叫料
            throw new MessageException("站点已存在物料，请勿重复叫料");
        }
        //该站点已经存在配送任务，请勿重复创建
        List<MapComponents> components = mapComponentsService.list(new LambdaQueryWrapper<MapComponents>().
                eq(MapComponents :: getLocationLock , Constants.LOCATION_LOCK).
                eq(MapComponents :: getLocation, point));
        if (!components.isEmpty()){
            //物料正在配送中，请勿重复叫料
            throw new MessageException("该站点已经存在配送任务，请勿重复创建");
        }
    }

    /**
     * 创建配送单
     */
    @Transactional
    public Map<String, Object> createDelivery(DeliverySave deliverySave) throws Exception{
        UserBean userBean = userUtils.getUserInfo();
        //根据拣配单号 查询拣配托盘信息 获取托盘 站点 拣配单id
//        List<PickContainer> resultContainers = pickContainerService.list(new LambdaQueryWrapper<PickContainer>().eq(
//                PickContainer::getPick, deliverySave.getPickId()).eq(PickContainer::getStatus, Constants.PICK_STATE_SUCCESS));
        List<PickContainer> resultContainers = pickContainerService.trayPriority(deliverySave.getPickId());
        if (resultContainers.isEmpty()){
            throw new MessageException("未找到托盘信息");
        }
        //PDA扫描站点码 查询对应的站点位置
        String point = qrcodeService.valueByCode(deliverySave.getEndStation(), Constants.CODE_TYPE_LOCATION);
        // 取第一个托盘
        PickContainer pickContainer = resultContainers.get(0);
        // 查找取货点和送货点是否在一个区域
        MapComponents pickLocation = mapComponentsService.getOne(new LambdaQueryWrapper<MapComponents>().eq(MapComponents::getLocation, pickContainer.getStation()));
        List<String> lockLocations = new ArrayList<>();
        lockLocations.add(pickLocation.getLocation());
        if(StringUtils.isNull(pickLocation)){
            throw new MessageException("取货站点不正确");
        }
        String pickZone = pickLocation.getZone();
        // 查找送货点 拣配的站点托盘类型和配送站点的托盘类型必须一致
        MapComponents deliveryLocation = mapComponentsService.getOne(new LambdaQueryWrapper<MapComponents>()
                .eq(MapComponents::getLocation, point).eq(MapComponents::getContainerType,
                        pickLocation.getContainerType()));
        if(StringUtils.isNull(deliveryLocation)){
            throw new MessageException("配送站点不正确");
        }
        // 变更托盘状态锁定
        pickContainerService.update(PickContainer.build(Integer.parseInt(Constants.DELIVERY_STATE_LOCK)),
                new LambdaQueryWrapper<PickContainer>().eq(PickContainer::getId, pickContainer.getId()));
        if(resultContainers.size() == 1){
            pickService.update(Pick.build(Constants.DELIVERY_STATE_LOCK),
                    new LambdaQueryWrapper<Pick>().eq(Pick::getId, pickContainer.getPick()));
        }
        lockLocations.add(deliveryLocation.getLocation());
        String deliveryZone = deliveryLocation.getZone();
        if(StringUtils.isEquals(pickZone, deliveryZone)){
            deliveryService.save(DeliverySave.build(Constants.DELIVERY_TYPE_DELIVERY, "0",
                    pickContainer.getStation(), point, null, deliverySave.getPickId(), Constants.AGV_TYPE_FORKLIFT,
                    pickContainer.getContainer(), pickZone));
        }else{
            // 已经被占用的站点
            List<String> lockStations = inventoryService.list(new LambdaQueryWrapper<Inventory>()
                    .isNotNull(Inventory::getStation).ne(Inventory::getStation, "")).stream().map(Inventory::getStation).collect(Collectors.toList());
            /// 查找配送可用站点
            LambdaQueryWrapper<MapComponents> queryWrapper = new LambdaQueryWrapper<MapComponents>().
                    eq(MapComponents::getClazz, Constants.COMPONENTS_TYPE_LOCATION).
                    eq(MapComponents::getLocationLock, false).
                    eq(MapComponents::getLocationWcCode, pickLocation.getRelayWcCode());
            if(StringUtils.isNotNull(lockStations) && !lockStations.isEmpty()){
                queryWrapper.notIn(MapComponents::getLocation, lockStations);
            }
            // 可用接力区站点
            List<MapComponents> availableRelayLocations = mapComponentsService.list(queryWrapper);
            if(availableRelayLocations.isEmpty()){
                throw new MessageException("接力区站点数量不足，请确认");
            }
            MapComponents relayLocation = availableRelayLocations.get(0);
            lockLocations.add(relayLocation.getLocation());

            List<String> availableEndLocations = availableRelayLocations.stream().map(MapComponents::getLocation).collect(Collectors.toList());
            // 查找要配送的点
            Map<String, String> convertEndStationMaps = mapComponentsConvertService.balanceStationByLocation(availableEndLocations);
            String agvType = StringUtils.isEquals(pickZone, Constants.PICK_AREA_LHGF)  ?
                    Constants.AGV_TYPE_FORKLIFT : Constants.AGV_TYPE_BALANCE;
            DeliverySave save = DeliverySave.build(Constants.DELIVERY_TYPE_DELIVERY, "0",
                    pickContainer.getStation(),
                    StringUtils.isEquals(pickZone, Constants.PICK_AREA_JSZX) ?
                            convertEndStationMaps.get(relayLocation.getLabel()) : relayLocation.getLocation(),
                    StringUtils.isEquals(deliveryZone, Constants.PICK_AREA_JSZX) ?
                            convertEndStationMaps.get(relayLocation.getLabel()) : relayLocation.getLocation(),
                    deliverySave.getPickId(),
                    agvType, pickContainer.getContainer(), pickZone);
            String deliveryId = deliveryService.save(save);
            save.setStartStation(relayLocation.getLocation());
            save.setEndStation(point);
            save.setStatus(Constants.DELIVERY_STATE_LOCK);
            save.setDeliveryContainer(null);
            save.setAgvType(StringUtils.isEquals(deliveryZone, Constants.PICK_AREA_LHGF)  ?
                    Constants.AGV_TYPE_FORKLIFT : Constants.AGV_TYPE_BALANCE);
            save.setParent(deliveryId);
            save.setZone(deliveryZone);
            deliveryService.save(save);
        }
        //修改lgs_map_components location_lock->true
        mapComponentsService.update(MapComponents.build(true), new LambdaUpdateWrapper<MapComponents>().in(MapComponents :: getLocation, lockLocations));
        // 获取班组code
        String wcCode = organizationService.wcCodeById(userBean.getPost());
        // 根据code查找站点区域
        List<PickWorkshop> pickWorkshops = pickService.pickByWcCodeAndArea(wcCode, null);
        Map<String, Object> result = new HashMap<>();
        result.put("size", pickWorkshops.size());
        return result;
    }
}